import csv
import math
import os
import random
import numpy as np
from sklearn.linear_model import LinearRegression
from loguru import logger


class Organism:
    DATA_DIR = 'data'

    def __init__(self, position, energy=10, genes=None, generation=0):
        self.position = position
        self.energy = energy
        self.alive = True
        self.generation = generation  # 记录出生世代

        # 从最新CSV文件获取优良基因作为初始基因
        if genes is None:
            self.genes = self._load_best_genes_from_latest_simulation()
        else:
            self.genes = genes

        self.vision_range = self.genes[0] + 1
        self.speed = self.genes[1] + 1
        self.reproduction_threshold = self.genes[2]

    def _load_best_genes_from_latest_simulation(self):
        """从最新模拟数据中提取优良基因（取平均+定向选择）"""
        file_path = self._get_latest_csv_file()
        if not file_path:
            # 无历史数据时随机生成（保留原逻辑）
            return [
                random.randint(0, 2),
                random.randint(0, 1),
                random.randint(15, 25)
            ]

        # 提取最后一代的平均性状
        last_gen_data = self._read_last_generation_data(file_path)
        if not last_gen_data:
            return self._random_genes()

        avg_vision, avg_speed = last_gen_data[:2]
        # 生成接近平均值的初始基因（允许±1范围）
        vision_gene = max(0, min(2, int(round(avg_vision)) + random.choice([-1, 0, 1])))
        speed_gene = max(0, min(1, int(round(avg_speed)) + random.choice([-1, 0])))  # 速度基因0-1
        threshold_gene = random.randint(15, 25)

        return [vision_gene, speed_gene, threshold_gene]

    def _get_latest_csv_file(self):
        """获取最新的CSV文件路径"""
        if not os.path.exists(self.DATA_DIR):
            return None
        csv_files = [f for f in os.listdir(self.DATA_DIR) if f.endswith('.csv')]
        if not csv_files:
            return None
        # 按修改时间排序，取最新
        csv_files.sort(key=lambda x: os.path.getmtime(os.path.join(self.DATA_DIR, x)), reverse=True)
        return os.path.join(self.DATA_DIR, csv_files[0])

    def _read_last_generation_data(self, file_path):
        """读取最后一代的平均视野和速度等级"""
        # logger.debug(f'Reading last generation data @ path: {file_path}')
        try:
            with open(file_path, 'r') as csvfile:
                reader = csv.DictReader(csvfile)
                data = list(reader)
                if not data:
                    return None
                last_row = data[-1]
                return (float(last_row['Average Vision Level']),
                        float(last_row['Average Speed Level']))
        except Exception as e:
            print(f"读取CSV文件出错: {e}")
            return None

    def _random_genes(self):
        """备用：随机生成初始基因（无历史数据时）"""
        return [
            random.randint(0, 2),   # 视野基因（0-2）
            random.randint(0, 1),   # 速度基因（0-1）
            random.randint(15, 25)  # 繁殖阈值（能量需求）
        ]

    def _find_nearby_foods(self, world):
        foods_in_vision = []
        x, y = self.position
        for food in world.foods:
            dx = abs(food.position[0] - x)
            dy = abs(food.position[1] - y)
            if dx <= self.vision_range and dy <= self.vision_range:
                foods_in_vision.append(food)
        return foods_in_vision

    def _get_best_move_target(self, world):
        foods = self._find_nearby_foods(world)
        if not foods:
            return self._get_random_moves(world)

        foods.sort(key=lambda f: math.hypot(f.position[0] - self.position[0], f.position[1] - self.position[1]))
        target_pos = foods[0].position

        moves = []
        x, y = self.position
        tx, ty = target_pos
        dx = tx - x
        dy = ty - y
        for step in range(self.speed):
            nx, ny = x, y
            if dx != 0:
                nx += 1 if dx > 0 else -1
                dx -= 1 if dx > 0 else +1
            elif dy != 0:
                ny += 1 if dy > 0 else -1
                dy -= 1 if dy > 0 else +1
            if 0 <= nx < world.width and 0 <= ny < world.height:
                moves.append((nx, ny))
                x, y = nx, ny
        return moves if moves else self._get_random_moves(world)

    def _get_random_moves(self, world):
        moves = []
        x, y = self.position
        for _ in range(self.speed):
            possible = [(x + dx, y + dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (dx, dy) != (0, 0)]
            possible = [pos for pos in possible if 0 <= pos[0] < world.width and 0 <= pos[1] < world.height]
            if possible:
                next_pos = random.choice(possible)
                moves.append(next_pos)
                x, y = next_pos
            else:
                break
        return moves

    def move(self, world):
        self.energy -= 1
        move_targets = self._get_best_move_target(world)
        for pos in move_targets:
            self.position = pos

        if self.energy <= 0:
            self.alive = False

    def eat(self, world):
        for food in world.foods[:]:
            if food.position == self.position:
                self.energy += food.energy
                world.foods.remove(food)
                break

    def _calculate_fitness(self, world, generations_alive):
        """综合适应度函数：存活代数 + 食物获取量 + 繁殖次数"""
        food_energy = sum(food.energy for food in self._find_nearby_foods(world))
        return generations_alive * 10 + food_energy + len(world.organisms) * 5  # 加权计算

    def reproduce(self, world):
        if self.energy >= self.reproduction_threshold:
            # 收集所有存活生物的基因和适应度
            X = np.array([org.genes for org in world.organisms])
            y = np.array([org._calculate_fitness(world, world.generations_alive) for org in world.organisms])

            # 训练线性回归模型
            model = LinearRegression()
            model.fit(X, y)

            # 获取基因对适应度的影响系数
            coefficients = model.coef_

            # 确定优良性状（正系数为有利，负系数为不利）
            favorable_genes = [i for i, coef in enumerate(coefficients) if coef > 0]
            unfavorable_genes = [i for i, coef in enumerate(coefficients) if coef < 0]

            # 选择繁殖亲本（优先高适应度个体）
            parents = self._select_parents(world, model, top_n=2)

            # 基因重组 + 定向变异
            new_genes = self._recombine_genes(parents)
            new_genes = self._apply_directional_mutation(new_genes, favorable_genes, unfavorable_genes)

            offspring = Organism(
                self.position,
                energy=10,
                genes=new_genes,
                generation=world.current_generation
            )
            world.organisms.append(offspring)
            self.energy -= 10

    def _select_parents(self, world, model, top_n=2):
        """根据适应度预测值选择优质亲本"""
        fitness_predictions = model.predict([org.genes for org in world.organisms])
        sorted_organisms = sorted(zip(world.organisms, fitness_predictions), key=lambda x: -x[1])
        return [org for org, _ in sorted_organisms[:top_n]]

    def _recombine_genes(self, parents):
        """基因重组：融合两个亲本的基因"""
        parent1, parent2 = parents
        new_genes = []
        for g1, g2 in zip(parent1.genes, parent2.genes):
            if random.random() < 0.5:
                new_genes.append(g1)
            else:
                new_genes.append(g2)
        return new_genes

    def _apply_directional_mutation(self, genes, favorable, unfavorable):
        """定向变异：有利基因降低变异率，不利基因提高变异率"""
        for i in range(len(genes)):
            if i in favorable:
                mutation_rate = 0.02  # 低变异率
            elif i in unfavorable:
                mutation_rate = 0.2  # 高变异率
            else:
                mutation_rate = 0.1  # 中性变异率

            if random.random() < mutation_rate:
                if i == 0:  # 视野基因（0-2）
                    genes[i] = random.randint(0, 2)
                elif i == 1:  # 速度基因（0-1）
                    genes[i] = random.randint(0, 1)
                else:  # 繁殖阈值基因
                    genes[i] = random.randint(15, 25)
        return genes